﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GcmpModelInterface.h"
#include "ElementId.h"

namespace gcmp
{
    class IDocument;
    class IGroup;
    class IElement;
    class IElementJoin;
    class IElementCut;
    class IElementOpening;
    class IJoinRelationshipBehavior;
    class ICutRelationshipBehavior;
    class IOpeningRelationshipBehavior;

    /// \brief 组内元素关系更新、维护工具。
    class GCMP_MODEL_INTERFACE_EXPORT GroupRelationshipUtils
    {
    public:
        /// \brief 调整连接关系
        /// \param pSrcJoinRelationshipBehavior 源对象的连接behavior
        /// \param pTargetJoinRelationshipBehavior 新拷贝对象的连接behavior
        /// \param pGroup 修改涉及到的组，这里的组可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \return true，更新成功；false，更新失败
        static bool AdjustJoinRelationship(const IJoinRelationshipBehavior* pSrcJoinRelationshipBehavior,
            IJoinRelationshipBehavior* pTargetJoinRelationshipBehavior, const IGroup* pGroup, bool isSyncFromTypeToGroup); 

        /// \brief 调整扣减关系
        /// \param pSrcCutRelationshipBehavior 源对象的扣减behavior
        /// \param pTargetCutRelationshipBehavior 新拷贝对象的扣减behavior
        /// \param pGroup 修改涉及到的组，这里的组可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \return true，更新成功；false，更新失败
        static bool AdjustCutRelationship(const ICutRelationshipBehavior* pSrcCutRelationshipBehavior,
            ICutRelationshipBehavior* pTargetCutRelationshipBehavior, const IGroup* pGroup, bool isSyncFromTypeToGroup);

        /// \brief 调整打洞关系
        /// \param pDoc 文档对象的指针
        /// \param pSrcElement 源对象的指针
        /// \param pTargetElement 新拷贝对象的指针
        /// \param pGroup 修改涉及到的组，这里的组可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \return true，更新成功；false，更新失败
        static bool AdjustOpeningRelationship(IDocument* pDoc, const IElement* pSrcElement, IElement* pTargetElement, const IGroup* pGroup, bool isSyncFromTypeToGroup);

        /// \brief 计算连接关系变化后，需要更新的数据与源数据的映射关系。当IElementJoin被添加、修改时调用。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementJoin 源连接对象的指针
        /// \param targetElemenJointId 新拷贝的连接对象的ElementId
        /// \param srcGroupId 源组\组类型的ElementId
        /// \param targetGroupId 目标组\组类型的ElementId
        /// \return 返回源对象与目标对象ElementId的映射表
        static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeElementJoinRelationships(IDocument* pDoc, const IElementJoin* pSrcElementJoin,
            const ElementId& targetElemenJointId, const ElementId& srcGroupId, const ElementId& targetGroupId);

        /// \brief 计算IElement（非几何关系元素）变化后，需要更新的数据与源数据的映射关系。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElement 源对象的指针
        /// \param targetElementId 新拷贝的对象的ElementId
        /// \param srcGroupId 源组\组类型的ElementId
        /// \param targetGroupId 目标组\组类型的ElementId
        /// \return 返回源对象与目标对象ElementId的映射表
        static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeElementRelationships(IDocument* pDoc, const IElement* pSrcElement,
            const ElementId& targetElementId, const ElementId& srcGroupId, const ElementId& targetGroupId);

        /// \brief 计算扣减关系变化后，需要更新的数据与源数据的映射关系。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementCut 源扣减对象的指针
        /// \param targetElementCutId 新拷贝的扣减对象的ElementId
        /// \param srcGroupId 源组\组类型的ElementId
        /// \param targetGroupId 目标组\组类型的ElementId
        /// \return 返回源对象与目标对象ElementId的映射表
        static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeElementCutRelationships(IDocument* pDoc, const IElementCut* pSrcElementCut,
            const ElementId& targetElementCutId, const ElementId& srcGroupId, const ElementId& targetGroupId);

        /// \brief 计算打洞关系变化后，需要更新的数据与源数据的映射关系。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementOpening 源打洞对象的指针
        /// \param targetElementOpeningId 新拷贝的打洞对象的ElementId
        /// \param srcGroupId 源组\组类型的ElementId
        /// \param targetGroupId 目标组\组类型的ElementId
        /// \return 返回源对象与目标对象ElementId的映射表
        static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeElementOpeningRelationships(IDocument* pDoc, const IElementOpening* pSrcElementOpening,
            const ElementId& targetElementOpeningId, const ElementId& srcGroupId, const ElementId& targetGroupId);

#pragma region ObsoleteAPI
        /// \brief 计算并替换连接关系。当IElementJoin被添加、修改时调用。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementJoin 源连接对象的指针
        /// \param targetElemenJointId 新拷贝的连接对象的ElementId
        /// \param groupId IGroup的ElementId。这里的IGroup可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param groupTypeId IGroupType的ElementId
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \param needUpdateElementIds 输出参数，需要被更新的元素的ElementId
        /// \return td::unordered_map<ElementId, ElementId, ElementIdHash>，返回新旧ElementId的映射表
        GCMP_OBSOLETE_API(2021 / 09 / 01, GroupRelationshipUtils::ComputeElementJoinRelationships(IDocument*, const IElementJoin*,
            const ElementId&, const ElementId&, const ElementId&)) static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeAndReplaceElementJoinRelationships(IDocument* pDoc, const IElementJoin* pSrcElementJoin,
            ElementId targetElemenJointId, ElementId groupId, ElementId groupTypeId, bool isSyncFromTypeToGroup, UnorderedElementIdSet& needUpdateElementIds);

        /// \brief 计算并替换IElement的相关关系。当IElement被修改后，它涉及的关系需要跟着更新。
        /// \param pDoc 文档对象的指针
        /// \param pSrcElement 源对象
        /// \param targetElementId 新拷贝对象的ElementId
        /// \param groupId IGroup的ElementId。这里的IGroup可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param groupTypeId IGroupType的ElementId
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \param needUpdateElementIds 输出参数，需要被更新的元素的ElementId
        /// \return td::unordered_map<ElementId, ElementId, ElementIdHash>，返回新旧ElementId的映射表
        GCMP_OBSOLETE_API(2021 / 09 / 01, GroupRelationshipUtils::ComputeElementRelationships(IDocument*, const IElement*,
            const ElementId&, const ElementId&, const ElementId&)) static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeAndReplaceElementRelationships(IDocument* pDoc, const IElement* pSrcElement,
            ElementId targetElementId, ElementId groupId, ElementId groupTypeId, bool isSyncFromTypeToGroup, UnorderedElementIdSet& needUpdateElementIds);

        /// \brief 计算并替扣减接关系。当IElementCut被添加、修改时调用
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementCut 源扣减对象的指针
        /// \param targetElementCutId 新拷贝的扣减对象的ElementId
        /// \param groupId IGroup的ElementId。这里的IGroup可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param groupTypeId IGroupType的ElementId
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \param needUpdateElementIds 输出参数，需要被更新的元素的ElementId
        /// \return td::unordered_map<ElementId, ElementId, ElementIdHash>，返回新旧ElementId的映射表
        GCMP_OBSOLETE_API(2021 / 09 / 01, GroupRelationshipUtils::ComputeElementCutRelationships(IDocument*, const IElementCut*,
            const ElementId&, const ElementId&, const ElementId&)) static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeAndReplaceElementCutRelationships(IDocument* pDoc, const IElementCut* pSrcElementCut,
            ElementId targetElementCutId, ElementId groupId, ElementId groupTypeId, bool isSyncFromTypeToGroup, UnorderedElementIdSet& needUpdateElementIds);

        /// \brief 计算并替扣打洞关系。当IElementOpening被添加、修改时调用
        /// \param pDoc 文档对象的指针
        /// \param pSrcElementOpening 源打洞对象的指针
        /// \param targetElementOpeningId 新拷贝的打洞对象的ElementId
        /// \param groupId IGroup的ElementId。这里的IGroup可能是从组到组类型更新的组，也可能是从组类型到组更新的组
        /// \param groupTypeId IGroupType的ElementId
        /// \param isSyncFromTypeToGroup 表示更新类型，是否从组类型到组的更新
        /// \param needUpdateElementIds 输出参数，需要被更新的元素的ElementId
        /// \return td::unordered_map<ElementId, ElementId, ElementIdHash>，返回新旧ElementId的映射表
        GCMP_OBSOLETE_API(2021 / 09 / 01, GroupRelationshipUtils::ComputeElementOpeningRelationships(IDocument*, const IElementOpening*,
            const ElementId&, const ElementId&, const ElementId& )) static std::unordered_map<ElementId, ElementId, ElementIdHash> ComputeAndReplaceElementOpeningRelationships(IDocument* pDoc, const IElementOpening* pSrcElementOpening,
            ElementId targetElementOpeningId, ElementId groupId, ElementId groupTypeId, bool isSyncFromTypeToGroup, UnorderedElementIdSet& needUpdateElementIds);
#pragma endregion ObsoleteAPI
    };
}



